#
# Copyright (c) 2023 supercell
#
# SPDX-License-Identifier: BSD-3-Clause
#

module Luce
  # Represents one kind of Markdown tag that can be parsed.
  abstract class InlineSyntax
    getter pattern : Regex

    # The first character of `pattern`, to be used as an efficient
    # first check that this syntax matches the current parser position.
    @start_character : Int32?

    # Creates a new `InlineSyntax` which matches text on *pattern*.
    #
    # If *start_character* is passed, it is used as a pre-matching
    # check which is faster than matching against *pattern*.
    #
    # If *case_sensitive* is disabled, then case is ignored when matching
    # the *pattern*.
    def initialize(pattern : String, @start_character : Int32? = nil, case_sensitive : Bool = true)
      regex_options = Regex::Options::MULTILINE
      regex_options |= Regex::Options::IGNORE_CASE unless case_sensitive
      @pattern = Regex.new(pattern, regex_options)
    end

    # Tries to match at the parser's current position.
    #
    # The parser's position can be overriden with *start_match_pos*.
    # Return whether or not the pattern successfully matched.
    def matches?(parser : InlineParser, start_match_pos : Int32? = nil) : Bool
      start_match_pos = parser.pos if start_match_pos.nil?

      # Before matching with the regular expression `pattern`, which
      # can be expensive on some platforms, check if even the first
      # character matches this syntax.
      if !@start_character.nil? &&
         parser.source.codepoint_at(start_match_pos) != @start_character
        return false
      end

      start_match = @pattern.match(parser.source, start_match_pos)
      return false if start_match.nil? || start_match.begin != start_match_pos

      # Write any existing plain text up to this point
      parser.write_text

      parser.consume(start_match[0].size) if on_match(parser, start_match)
      true
    end

    # Processes *match*, adding nodes to *parser* and possibly advancing
    # *parser*.
    #
    # Will return whether the caller should advance *parser* by
    # `match[0].size`.
    abstract def on_match(parser : InlineParser, match : Regex::MatchData) : Bool
  end
end
